#include "image.h"
#include<QImage>
#include<QDebug>
#include<QLabel>
#include <QTime>
#include<math.h>
#pragma pack(push, 1)
struct BMPFILEHEADER {
    unsigned short bfType;
    unsigned int bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned int bfOffBits;
};
#pragma pack(pop)

struct BITMAPINFOHEADER {
    unsigned long biSize;            //本结构所占用字节数 40字节
    long biWidth;           //位图的宽度，以像素为单位
    long biHeight;          //位图的高度，以像素为单位
    unsigned short biPlanes;          //目标设备的级别，必须为1
    unsigned short biBitCount;        //每个像素所需的位数，必须是1（双色）、
    //4（16色）、8（256色）或24（真彩色）之一
    unsigned long biCompression;     //位图压缩类型，必须是 0（BI_RGB不压缩）、
    //1（BI_RLE8压缩类型）
    //2（BI_RLE压缩类型）之一
    unsigned long biSizeImage;       //位图的大小，以字节为单位
    long biXPelsPerMeter;   //位图水平分辨率，每米像素数
    long biYPelsPerMeter;   //位图垂直分辨率，每米像素数
    unsigned long biClrUsed;         //位图实际使用的颜色表中的颜色数
    unsigned long biClrImportant;    //位图显示过程中重要的颜色数
};

double *Image::NewData(int h, int w, double value) {
    double *temp;
    temp = new double[h * w * 3];
    for (int i = 0; i < h * w * 3; i++) {
        temp[i] = value;
    }
    return temp;
}

double &Image::At(int row, int col) const {

    return data[(row * width )*3+ col];
}

Image::Image(QObject *parent)
    : QObject{parent}
{

}

Image::Image() {
    height = 0;
    width = 0;
    data = nullptr;
}

Image::Image(const char *ImageName) {
    ReadBMP(ImageName);
}

//Image::Image(const QImage& qimg){
//    height = qimg.height();
//    width = qimg.width();
////    qDebug()<<"height="<<height<<Qt::endl<<"width="<<width;
//    const uchar* temp_data = qimg.convertToFormat(QImage::Format_BGR888).bits();
//    data = NewData(height,width);
//    int place=0;

//    for(int i=height-1;i>=0;i--){
//        for (int j=0;j<width*3;j++)
//        {
//            data[place++]=temp_data[i*width*3+j];
//        }
//    }
//}

//Image::Image(const QLabel& lab){
//    QImage qimg = lab.pixmap()->toImage();
//    height = qimg.height();
//    width = qimg.width();
////    qDebug()<<"height="<<height<<Qt::endl<<"width="<<width;
//    const uchar* temp_data = qimg.convertToFormat(QImage::Format_BGR888).bits();
//    data = NewData(height,width);
//    int place=0;
//    qDebug()<<"h and w";
//    qDebug()<<height;
//    qDebug()<<width;
//    QTime *time=new QTime();
//    QString strTime;
//    strTime=time->currentTime().toString("hh:mm:ss");
//    qDebug()<<strTime;
//    for(int i=height-1;i>=0;i--){
//        qDebug()<<place;
//        for (int j=0;j<width*3;j++)
//        {
//            data[place++]=temp_data[i*width*3+j];
//        }
//    }
//    strTime=time->currentTime().toString("hh:mm:ss");
//    qDebug()<<strTime;
//    qDebug()<<"9";
//}

Image::~Image() {
    delete [] data;
}

//从硬盘读入图像文件;
void Image::ReadBMP(const char *filename) {
    FILE *fp;
    BMPFILEHEADER file_header{};
    BITMAPINFOHEADER info_header{};
    fopen_s(&fp, filename, "rb");
    // 读取文件头
    fread(&file_header, sizeof(BMPFILEHEADER), 1, fp);

    // 读取信息头
    fread(&info_header, sizeof(BITMAPINFOHEADER), 1, fp);

    height = info_header.biHeight;
    width = info_header.biWidth;
    //处理不是4的整数的情况
    if (width % 4 != 0) {
        width = (width * info_header.biBitCount / 8 + 3) / 4 * 4;
        width /= 3;
    }
    //    if (height % 4 != 0) {
    //        height = (width * info_header.biBitCount / 8 + 3) / 4 * 4;
    //        height /= 3;
    //    }
    // 计算总的像素个数
    int num_pix = width * height;
    //开辟临时空间储存数据
    auto *temp_data = new unsigned char[num_pix * 3];
    fread(temp_data, num_pix * 3, 1, fp);
    int place = 0;
    data = NewData(height, width);
    //将数据存储到二维数组中
    for (int i = 0; i < height * width * 3; i++) {
        data[i] = temp_data[place++];
    }
    delete[] temp_data;
}

//保存图像;
void Image::WriteBMP(const char *filename) {
    BMPFILEHEADER file_header{};
    BITMAPINFOHEADER info_header{};
    file_header.bfType = 19778;
    file_header.bfSize = 54 + width * height;
    file_header.bfReserved1 = 0;
    file_header.bfReserved2 = 0;
    file_header.bfOffBits = 54;

    info_header.biSize = 40;
    info_header.biWidth = width;
    info_header.biHeight = height;
    info_header.biPlanes = 1;
    info_header.biBitCount = 24;
    info_header.biCompression = 0;
    info_header.biSizeImage = width * height;
    info_header.biXPelsPerMeter = 0;
    info_header.biYPelsPerMeter = 0;
    info_header.biClrUsed = 0;
    info_header.biClrImportant = 0;

    FILE *fp;
    int i, j, offset_bytes = 0;
    if ((width + offset_bytes) % 4 != 0)
        offset_bytes++;

    // 二进制写入方式打开文件
    fopen_s(&fp, filename, "wb");
    // 写入文件头
    fwrite(&file_header, sizeof(BMPFILEHEADER), 1, fp);
    // 写入信息头
    fwrite(&info_header, sizeof(BITMAPINFOHEADER), 1, fp);
    // 写入数据
    unsigned char t;
    for (i = 0; i < height; i++) {
        for (j = 0; j < (width + offset_bytes) * 3; j += 3) {
            if (j < width * 3) {
                if (data[i * width + j] < 0)
                    data[i * width + j] = 0;
                if (data[i * width + j] > 255)
                    data[i * width + j] = 255;
                for (int k = 0; k < 3; k++) {
                    t = (unsigned char) data[i * width * 3 + j + k];
                    fwrite(&t, 1, 1, fp);
                };

            } else {
                t = 0;
                fwrite(&t, 1, 1, fp);
            }

        }
    }
    // 关闭写入的文件
    fclose(fp);
}

Image::Image(int h, int w) {
    height = h;
    width = w;
    data = NewData(height, width);
}

//拷贝构造函数
Image::Image(const Image &im) {
    height = im.height;
    width = im.width;
    data = NewData(height, width);
    for (int i = 0; i < height * width * 3; ++i) {
        data[i] = im.data[i];
    }
}
const uchar* Image::GetData(){
    auto* new_data = new uchar[height*width*3];
    int place =0;
    for(int i=height-1;i>0;i--){
        for(int j = 0;j<width*3;j+=3){
            new_data[place++]=(uchar)data[i*width*3+j+2];
            new_data[place++]=(uchar)data[i*width*3+j+1];
            new_data[place++]=(uchar)data[i*width*3+j];
        }
    }
    return new_data;
}

int Image::Height ()const{
    return height;
}

int Image::Width ()const{
    return width;
}
//图像旋转的函数
void Image::Rotate(int degree) {
    int times = 0;
    if (degree < 0)
        times = -(degree / 90) * 3;
    else
        times = degree / 90;
    int new_width, new_height;
    int i, j;
    double *new_data;
    for (int k = 0; k < times; k++) {
        new_width = height;
        new_height = width;
        new_data = NewData(new_height, new_width);
        for (i = 0; i < height; i++) {
            for (j = 0; j < width * 3; j += 3) {
                new_data[j * height + (height - i - 1) * 3] = data[i * width * 3 + j];
                new_data[j * height + (height - i - 1) * 3+1] = data[i * width * 3 + j+1];
                new_data[j * height + (height - i - 1) * 3+2] = data[i * width * 3 + j+2];


            }
        }
        //        DeleteData(height, data);
        delete[] data;
        data = new_data;
        width = new_width;
        height = new_height;
    }
}

void Image::Resize(int h, int w) {
    int i, j,w1=w;
    //处理不是4的整数的情况
    //    if (width % 4 != 0) {
    //        width = (width * 3 + 3) / 4 * 4;
    //        width /= 3;
    //    }
    if(w  % 4 !=0 ){
        for(w1=w;w1%4!=0;++w1);
    }
    double *new_data = NewData(h, w1);
    qDebug()<<"wi="<<w1;
    qDebug()<<"w="<<w;
    qDebug()<<"width"<<width;

    double h_scale = (double) height /h , w_scale = (double) width /w1 ;
    int pre_i=0, pre_j=0;
    for (i = 0; i < h; i++) {
        for (j = 0; j < w1 ; j++) {
            pre_i = (int) (i * h_scale);
            pre_j = (int) (j * w_scale);
            if (pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width) {
                new_data[i * 3 * w1 + j*3] = data[pre_i * 3 * width + pre_j*3];
                new_data[i * 3 * w1 + j*3 + 1] = data[pre_i * 3 * width + pre_j*3 + 1];
                new_data[i * 3 * w1 + j*3 + 2] = data[pre_i * 3 * width + pre_j*3+ 2];
            }
        }
        if(w1!=w)
        {
            for(int k=w*3;k<w1*3;k++)
                new_data[i*3*w1+k]=0;
        }
    }
    delete[] data;
    data = new_data;
    height = h;
    width = w1;
}

Image::operator QImage (){
    auto* new_data = new uchar[height*width*3];
    int place =0;
    for(int i=height-1;i>=0;i--){
        for(int j = 0;j<width*3;j++){
            new_data[place++]=(uchar)data[i*width*3+j];
        }
    }
    QImage qimg(new_data,width,height,QImage::Format_BGR888);
    return qimg;
}

template<class T>
void Simple_swap(T &a, T &b) {
    T t;
    t = a;
    a = b;
    b = t;
}

void Image::DrawLine(int x1,int y1,int x2,int y2,int r,RGB* rgb){
    if(x1>=0&&x1<width&&y1<height&&y1>=0&&x2<width&&x2>=0&&y2<height&&y2>=0){

        for(int i=std::min(y1,y2);i<=std::max(y1,y2);i++)
        {
            for(int j=std::min(x1,x2);j<=std::max(x1,x2);j++)
                if((j-x1)*(y1-y2)==((x1-x2)*(i-y1))){
//                    data[i*width*3+j*3]=rgb->b;
//                    data[i*width*3+j*3+1]=rgb->g;
//                    data[i*width*3+j*3+2]=rgb->r;
                    DrawCircle(j,i,r,rgb);
                }

        }
    }
    else
        return;

}


void  Image::DrawCircle(int x,int y,int r,RGB* rgb ){
    if(x-r>=0&&y-r>=0&&x+r<= width &&y+r<=height){
        for(int i=y-r;i<=y+r;++i)
            for(int j=x-r;j<=x+r;++j){
                if((i-y)*(i-y)+(j-x)*(j-x)<=r*r)
                {
                    data[i*width*3+j*3]=rgb->b;
                    data[i*width*3+j*3+1]=rgb->g;
                    data[i*width*3+j*3+2]=rgb->r;
                }
            }
    }
}



void Image::gray()
{
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width*3; j+=3) {
            int average=data[i*3*width+j]*0.114+data[i*3*width+j+1]*0.5870+data[i*3*width+j+2]*0.299;
            data[i*3*width+j]=average;
            data[i*3*width+j+1]=average;
            data[i*3*width+j+1]=average;
        }
    }
}
void Image::sobel()
{
        int i,j;
        double *data1=NewData(height,width);
        double *data2=NewData(height,width);
        for (i = 1; i < height-1; i++)
        {
            for (j = 1; j < width-1; j++)
            {
                int temp1 = 0;                                      //-2 0 2
                temp1 -= data[((i-1)*width+(j-1))*3];               //-1 0 1      该算子可以求出水平方向的导数
                temp1 -= data[i*3*width+j*3-3]* 2;
                temp1 -= data[(i+1)*3*width+j*3-3];
                temp1 += data[(i-1)*3*width+j*3+3];
                temp1 += data[i*3*width+j*3+3] * 2;
                temp1 += data[(i+1)*3*width+j*3+3];
                if (temp1 < 0) temp1 = -temp1;
                if (temp1 > 255) temp1 = 255;
                data1[i*3*width+j*3]=temp1;
                data1[i*3*width+j*3+1]=temp1;
                data1[i*3*width+j*3+2]=temp1;
            }
        }
        for (i = 1; i < height-1; i++)
        {
            for (j = 1; j < width-1; j++)
            {
                int temp1 = 0;                                      //-2 0 2
                temp1 -= data[((i-1)*width+(j-1))*3];               //-1 0 1      该算子可以求出水平方向的导数
                temp1 -= data[(i-1)*3*width+j*3]* 2;
                temp1 -= data[(i-1)*3*width+j*3+3];
                temp1 += data[(i+1)*3*width+j*3-3];
                temp1 += data[(i+1)*3*width+j*3] * 2;
                temp1 += data[(i+1)*3*width+j*3+3];
                if (temp1 < 0) temp1 = -temp1;
                if (temp1 > 255) temp1 = 255;
                data2[i*3*width+j*3]=temp1;
                data2[i*3*width+j*3+1]=temp1;
                data2[i*3*width+j*3+2]=temp1;
            }
        }
        for (i = 1; i < height-1; i++)
        {
            for (j = 1; j < width-1; j++)
            {
               int temp=sqrt(data1[(i*width+j)*3]*data1[(i*width+j)*3]+data2[(i*width+j)*3]*data2[(i*width+j)*3]);
               data[(i*width+j)*3]=temp;
               data[(i*width+j)*3+1]=temp;
               data[(i*width+j)*3+2]=temp;
            }
        }
        delete [] data1;
        delete [] data2;
}
//false 左右，true 上下;
void Image::Flip(int code)
{
    if (code == 0)
    {//左右翻转
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width / 2; j++) {
                for (int t = 0; t < 3; t++)
                {
                    Simple_swap(data[(i*width+j)*3+t],data[(i * width + width-j-1) * 3 + t]);
                }
            }
        }
    } else {
        for (int i = 0; i < height / 2; i++) {
            for (int j = 0; j < width; j++) {
                for(int t=0;t<3;t++)
                    Simple_swap(data[(i * width + j) * 3 + t],data[t+j*3+(height-i-1)*3*width]);
            }
        }
    }
}

void Image::WriteText(const char *filename) {
    FILE *fp;
    fopen_s(&fp, filename, "w");
    fprintf(fp, "%d %d ", height, width);
    for (int i = 0; i < height; i++)
        for (int j = 0; j < width*3; j++) {
            fprintf(fp, "%lf ", data[i*width +j]);
        }
    fclose(fp);
}

void Image::ShowImage(QLabel* wid)
{
    wid->setPixmap(QPixmap::fromImage((QImage)(*this)));
}

void Image::BrightContrast(int bri,int con,const Image* ori_img){
    for(int i=0;i<height*width*3;i++){
        double d = con*0.01*(ori_img->data[i])+bri;
        if(d>255)
            d=255;
        if(d<0)
            d=0;
        data[i]=d;
    }
}
void Image::Normalize() {
    double min = data[0], max = data[0];
    int i;
    for (i = 0; i < height * width * 3; i++)
    {
        if (max < data[i]) { max = data[i]; }
        if (min > data[i]) { min = data[i]; }
    }
    if (max != 0)
    {
        for (i = 0; i < height * width * 3; i++)
        {
            double num;
            num = data[i];
            if (num == min) { data[i] = 0; }
            else if (num == max) { data[i] = 1; }
            else { data[i] = (num - min) / max; }
        }
    }
}

Image Image::operator-() {
    double min = data[0], max = data[0];
    int i;
    for (i = 0; i < height * width * 3; i++)
    {
        if (min > data[i]) { min = data[i]; }
        if (max < data[i]) { max = data[i]; }
    }
    Normalize();
    for (i = 0; i < height * width * 3; i++)
    {
        data[i] = 1 - data[i];
        data[i] = data[i] * max + min;
    }
    return (*this);
}
void Image::Cut(int x1, int y1, int x2, int y2) {
    int i, j, row = 0, col = 0;
    int new_width = x2 - x1 + 1, new_height = y2 - y1 + 1;
    for(;new_width%4!=0;x2++,new_width++);
    double *new_data = NewData(new_height, new_width);
    for (i = y1; i <= y2; i++) {
        for (j = x1 * 3; j <= x2 * 3; j+=3) {
            new_data[row * new_width*3+ (col)] = data[i * width * 3 + j];
            new_data[row * new_width*3+ col+1] = data[i * width * 3 + j+1];
            new_data[row * new_width*3+ col+2] = data[i * width * 3 + j+2];
            col+=3;

        }
        row++;
        col = 0;
    }
    delete[] data;
    data = new_data;
    width = new_width;
    height = new_height;
}
